﻿using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;

namespace LIK.NetworkTools
{
    internal static class IpHelper
    {

        public static bool IpBelongsToSubnet(this IPAddress subnet, IPAddress ipAddress)
        {
            try 
            {
                Byte[] addressOctets = ipAddress.GetAddressBytes();
                Byte[] networkOctets = subnet.GetAddressBytes();

                return
                    (networkOctets[0] == addressOctets[0]) &&
                    (networkOctets[1] == addressOctets[1]) &&
                    (networkOctets[2] == addressOctets[2]);
            }
            catch 
            {
                return false;
            }
        }

        public static bool IpBelongsToSubnet(this IPAddress subnet, IPAddress mask, IPAddress address)
        {
            try
            {
                //IPAddress Subnet = IPAddress.Parse(subnet);
                //IPAddress Mask = IPAddress.Parse(mask);
                //IPAddress Address = IPAddress.Parse(address);

                Byte[] addressOctets = address.GetAddressBytes();
                Byte[] subnetOctets = mask.GetAddressBytes();
                Byte[] networkOctets = subnet.GetAddressBytes();

                return
                    ((networkOctets[0] & subnetOctets[0]) == (addressOctets[0] & subnetOctets[0])) &&
                    ((networkOctets[1] & subnetOctets[1]) == (addressOctets[1] & subnetOctets[1])) &&
                    ((networkOctets[2] & subnetOctets[2]) == (addressOctets[2] & subnetOctets[2])) &&
                    ((networkOctets[3] & subnetOctets[3]) == (addressOctets[3] & subnetOctets[3]));
            }
            catch 
            {
                return false;
            }
        }

        /// <summary>
        /// Returns true if the specified IP address is a multicast address
        /// </summary>
        /// <param name="ip"></param>
        /// <returns></returns>
        internal static bool IsIpMulticast(this IPAddress ip)
        {
            bool result = true;
            if (!ip.IsIPv6Multicast)
            {
                byte highIp = ip.GetAddressBytes()[0];
                if (highIp < 224 || highIp > 239)
                {
                    result = false;
                }
            }
            return result;
        }

        /// <summary>
        /// Gets first found IP address of the current PC
        /// </summary>
        /// <returns></returns>
        public static IPAddress GetLocalIpAddress()
        {
            String strHostName = Dns.GetHostName();
            IPHostEntry ipEntry = Dns.GetHostEntry(strHostName);
            IPAddress[] addr = ipEntry.AddressList;
            foreach (IPAddress ip in ipEntry.AddressList.Where(ip => ip.AddressFamily == AddressFamily.InterNetwork))
            {
                if (!ip.IsIPv6LinkLocal)
                {
                    return (ip);
                }
            }
            return addr.Length > 0 ? addr[0] : null;
        }

        /// <summary>
        /// Gets first found MAC address of the current PC.
        /// </summary>
        /// <returns></returns>
        internal static PhysicalAddress GetLocalMacAddress()
        {
            foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces())
            {
                // Only consider Ethernet network interfaces
                if (nic.NetworkInterfaceType == NetworkInterfaceType.Ethernet &&
                    nic.OperationalStatus == OperationalStatus.Up)
                {
                    return nic.GetPhysicalAddress();
                }
            }
            return null;
        }

        /// <summary>
        /// Get computer INTERNET address like 93.136.91.7
        /// </summary>
        /// <returns></returns>
        internal static string GetLocalHostInternetGateIp()
        {
            // check IP using DynDNS's service
            WebRequest request = WebRequest.Create("http://checkip.dyndns.org");
            WebResponse response = request.GetResponse();
            using (StreamReader stream = new StreamReader(response.GetResponseStream()))
            {
                // IMPORTANT: set Proxy to null, to drastically INCREASE the speed of request
                request.Proxy = null;

                // read complete response
                string ipAddress = stream.ReadToEnd();

                // replace everything and keep only IP
                return ipAddress.
                    Replace("<html><head><title>Current IP Check</title></head><body>Current IP Address: ", string.Empty)
                    .
                    Replace("</body></html>", string.Empty);
            }
        }


        private const int PING_TIMEOUT = 500;

        public static bool IsHostAccessible(string hostNameOrAddress)
        {
            var ping = new Ping();
            var reply = ping.Send(hostNameOrAddress, PING_TIMEOUT);
            return reply != null && reply.Status == IPStatus.Success;
        }

        public static bool IsHostAccessible(this IPAddress ipAddress)
        {
            var ping = new Ping();
            var reply = ping.Send(ipAddress, PING_TIMEOUT);
            return reply != null && reply.Status == IPStatus.Success;
        }

        public const string NotFound = "not found";

        /// <summary>
        /// Works slow if IP doesn't exist
        /// </summary>
        /// <param name="address"></param>
        /// <param name="macAddress"></param>
        /// <returns></returns>
        public static bool GetMacAddress_(IPAddress address, out string macAddress)
        {
            int intAddress = BitConverter.ToInt32(address.GetAddressBytes(), 0);

            byte[] macAddr = new byte[6];
            int macAddrLen = macAddr.Length;
            if (NativeMethods.SendARP(intAddress, 0, macAddr,ref macAddrLen) != 0)
            {
                macAddress = NotFound; //"(NO ARP result)";
                return false;
            }

            macAddress = macAddr.ToReadable();

            return true;
        }

        public static bool GetIpMacAddress(this IPAddress addr, out string macAddress)
        {
            string ipAddress = addr.ToString();
            macAddress = string.Empty;
            try
            {
                var pProcess = new Process
                {
                    StartInfo =
                    {
                        FileName = "arp",
                        Arguments = "-a " + ipAddress,
                        UseShellExecute = false,
                        RedirectStandardOutput = true,
                        CreateNoWindow = true
                    }
                };
                pProcess.Start();
                string strOutput = pProcess.StandardOutput.ReadToEnd().ToUpper();
                string[] substrings = strOutput.Split('-');
                if (substrings.Length >= 8)
                {
                    macAddress = substrings[3].Substring(Math.Max(0, substrings[3].Length - 2))
                                 + ":" + substrings[4] + ":" + substrings[5] + ":" + substrings[6]
                                 + ":" + substrings[7] + ":" + substrings[8].Substring(0, 2);
                    return true;
                }
                return false;
            }
            catch
            {
                return false;
            }
        }

        #region The system cannot find the file specified

        /*
        public static bool GetMacAddress(IPAddress adr, out string macAddress)
        {
            string ipAddress = adr.ToString();
            macAddress = string.Empty;

            try
            {
                ProcessStartInfo processStartInfo = new ProcessStartInfo();
                Process process = new Process();
                processStartInfo.FileName = @"C:\Windows\System32\nbtstat.exe";
                processStartInfo.RedirectStandardInput = false;
                processStartInfo.RedirectStandardOutput = true;
                processStartInfo.Arguments = "-a " + ipAddress;
                processStartInfo.UseShellExecute = false;
                process = Process.Start(processStartInfo);

                int Counter = -1;
                while (Counter <= -1)
                {
                    // Look for the words "mac address" in the output.
                    // The output usually looks likes this:
                    // Local Area Connection:
                    //Node IpAddress: [13.15.111.222] Scope Id: []
                    //           NetBIOS Remote Machine Name Table
                    //       Name               Type        Status
                    //    --------------------------------------------
                    //    SAMGLS0790W    <00>  UNIQUE      Registered
                    //    SAMPLS0790W    <20>  UNIQUE      Registered
                    //    NA            <00>  GROUP       Registered
                    //    MAC Address = 00-21-70-2B-A5-43

                    Counter = macAddress.Trim().ToLower().IndexOf("mac address", 0);
                    if (Counter > -1)
                    {
                        break;
                    }
                    macAddress = process.StandardOutput.ReadLine();
                }
                process.WaitForExit();
                macAddress = macAddress.Trim();
            }
            catch (Exception e)
            {
                // Something unexpected happened? Inform the user
                // The possibilities are:
                // 1.That the machine is not on the network currently
                // 2. The IP address/hostname supplied are not on the same network
                // 3. The host was not found on the same subnet or could not be
                //    resolved
                //Console.WriteLine("Failed because:" + e.ToString());
            }
            return true;
        }
        */

        #endregion

    }

    public static class Extender
    {
        public static string ToReadable(this byte[] macAddr)
        {
            int macAddrLen = macAddr.Length;
            string[] str = new string[(int)macAddrLen];
            for (int i = 0; i < macAddrLen; i++)
            {
                str[i] = macAddr[i].ToString("x2");
            }
            string macAddress = string.Join(":", str);
            return macAddress.ToUpper();
        }
    }

}
